home *** CD-ROM | disk | FTP | other *** search
/ Aminet 24 / Aminet 24 (1998)(GTI - Schatztruhe)[!][Apr 1998].iso / Aminet / util / sys / CopyReplace.lha / Source / Copy.c next >
Encoding:
C/C++ Source or Header  |  1998-01-10  |  29.0 KB  |  1,084 lines

  1. #define NAME         "Copy"
  2. #define REVISION     "13"
  3. #define DISTRIBUTION "(Freeware) "
  4. //#define DEBUG
  5.  
  6. /* Programmheader
  7.  
  8.         Name:           Copy
  9.         Author:         SDI
  10.         Distribution:   Freeware
  11.         Description:    copies, moves, deletes and links files
  12.         Compileropts:   -
  13.         Linkeropts:     -gsi -l amiga
  14.  
  15.  1.0   02.08.97 : first version, only raw source
  16.  1.1   06.08.97 : added more stuff
  17.  1.2   08.08.97 : completed option description, added new code
  18.  1.3   09.08.97 : and more stuff done and SILENT option
  19.  1.4   11.08.97 : fixed problem with DELETE and ExNext function
  20.  1.5   14.08.97 : fixed DoWork, added new stuff
  21.  1.6   17.08.97 : added more functions, program is near completion
  22.  1.7   24.08.97 : bug fixes in DoWork function
  23.  1.8   25.09.97 : included missing functions, started debugging
  24.  1.9   26.09.97 : debugging, source cleanup
  25.  1.10  28.09.97 : and still debugging
  26.  1.11  30.09.97 : fixed some errors
  27.  1.12  04.10.97 : Added FORCE Option
  28.  1.13  19.12.97 : added DIRECT option
  29. */
  30.  
  31. #include <proto/dos.h>
  32. #include <proto/exec.h>
  33. #include <exec/memory.h>
  34. #include "SDI_defines.h"
  35. #define SDI_TO_ANSI
  36. #include "SDI_ASM_STD_protos.h"
  37.  
  38. #define PARAM   "FROM/M,TO,PAT=PATTERN/K,BUF=BUFFER/K/N,ALL/S,"         \
  39.                 "DIRECT/S,CLONE/S,DATES/S,NOPRO/S,COM=COMMENT/S,"       \
  40.                 "QUIET/S,SILENT/S,NOREQ/S,ERRWARN/S,MAKEDIR/S,"         \
  41.                 "MOVE/S,DELETE/S,HARD=HARDLINK/S,SOFT=SOFTLINK/S,"      \
  42.                 "FOLNK=FORCELINK/S,FODEL=FORCEDELETE/S,"                \
  43.                 "FOOVR=FORCEOVERWRITE/S,DONTOVR=DONTOVERWRITE/S,"    \
  44.                 "FORCE/S"
  45.  
  46. #define COPYFLAG_ALL            (1<<0)
  47. #define COPYFLAG_DATES          (1<<1)
  48. #define COPYFLAG_NOPRO          (1<<2)
  49. #define COPYFLAG_COMMENT        (1<<3)
  50. #define COPYFLAG_FORCELINK      (1<<4)
  51. #define COPYFLAG_FORCEDELETE    (1<<5)
  52. #define COPYFLAG_FORCEOVERWRITE (1<<6)
  53. #define COPYFLAG_DONTOVERWRITE  (1<<7)
  54. #define COPYFLAG_QUIET          (1<<8)
  55. #define COPYFLAG_SILENT         (1<<9)
  56. #define COPYFLAG_ERRWARN    (1<<10)
  57.  
  58. #define COPYFLAG_SOFTLINK       (1<<20) /* produce softlinks */
  59. #define COPYFLAG_DEST_FILE      (1<<21) /* one file mode */
  60. #define COPYFLAG_DONE           (1<<22) /* did something in DoWork */
  61. #define COPYFLAG_ENTERSECOND    (1<<23) /* entered directory second time */
  62.  
  63. #define COPYMODE_COPY           0
  64. #define COPYMODE_MOVE           1
  65. #define COPYMODE_DELETE         2
  66. #define COPYMODE_MAKEDIR    3
  67. #define COPYMODE_LINK           4
  68.  
  69. #define PRINTOUT_SIZE           50      /* maximum size of name printout */
  70. #define PRINTOUT_SPACES         10      /* maximum number of spaces      */
  71.  
  72. #define FILEPATH_SIZE           300     /* maximum size of filepaths     */
  73.  
  74. /* return values */
  75. #define TESTDEST_DIR_OK         2       /* directory exists, go in */
  76. #define TESTDEST_DELETED        1       /* file or empty directory deleted */
  77. #define TESTDEST_NONE           0       /* nothing existed */
  78. #define TESTDEST_ERROR          -1      /* an error occured */
  79. #define TESTDEST_CANTDELETE     -2      /* deletion not allowed (DONTOV) */
  80.  
  81. struct Args {
  82.   STRPTR *      from;
  83.   STRPTR        to;
  84.   STRPTR        pattern;
  85.   LONG *        buffer;
  86.   LONG          all;
  87.   LONG        direct;
  88.   LONG          clone;
  89.   LONG          dates;
  90.   LONG          nopro;
  91.   LONG          comment;
  92.   LONG          quiet;
  93.   LONG          silent;
  94.   LONG          noreq;
  95.   LONG        errwarn;
  96.   LONG        makedir;
  97.   LONG          move_mode;
  98.   LONG          delete_mode;
  99.   LONG          hardlink;
  100.   LONG          softlink;
  101.   LONG          forcelink;
  102.   LONG          forcedelete;
  103.   LONG          forceoverwrite;
  104.   LONG          dontoverwrite;
  105.   LONG        force;
  106. };
  107.  
  108. struct CopyData {
  109.   ULONG         Flags;
  110.   ULONG         BufferSize;
  111.   STRPTR        Pattern;
  112.   ULONG         Destination;
  113.   ULONG         CurDest;  /* Current Destination */
  114.   ULONG        DestPathSize;
  115.   struct FileInfoBlock *Fib;
  116.   UBYTE         Mode;
  117.   UBYTE         RetVal;         /* when set, error output is already done */
  118.   UBYTE         RetVal2;        /* when set, error output must be done */
  119.   UBYTE         Deep;
  120.   UBYTE        FileName[FILEPATH_SIZE];
  121.   UBYTE        DestName[FILEPATH_SIZE];
  122. };
  123.  
  124. #define TEXT_READ        texts[0]
  125. #define TEXT_COPIED             texts[1]
  126. #define TEXT_MOVED              texts[2]
  127. #define TEXT_DELETED            texts[3]
  128. #define TEXT_LINKED             texts[4]
  129. #define TEXT_RENAMED            texts[5]
  130. #define TEXT_CREATED            texts[6]
  131. #define TEXT_ENTERED            texts[7]
  132. #define TEXT_OPENED_FOR_OUTPUT  texts[8]
  133. #define TEXTNUM_MODE            9
  134. #define TEXT_DIRECTORY          texts[15]
  135. #define TEXT_NOT_DONE           texts[16]
  136. #define TEXT_NOTHING_DONE       texts[17]
  137. #define TEXT_ERR_FORCELINK      texts[18]
  138. #define TEXT_ERR_DELETE_DEVICE  texts[19]
  139. #define TEXT_ERR_DEST_DIR       texts[20]
  140. #define TEXT_ERR_INFINITE_LOOP  texts[21]
  141. #define TEXT_ERR_WILDCARD_DEST  texts[22]
  142.  
  143. STRPTR texts[] =
  144. {
  145. "read",
  146. "copied",
  147. "moved",
  148. "deleted",
  149. "linked",
  150. "renamed",
  151. "created",
  152. "entered",
  153. "opened for output",
  154. "COPY mode\n",
  155. "MOVE mode\n",
  156. "DELETE mode\n",
  157. "MAKEDIR mode\n",
  158. "HARDLINK mode\n",
  159. "SOFTLINK mode\n",
  160. "%s <Dir>",                /* output of directories */
  161. "not %s: ",
  162. "No file was processed.\n",
  163. "FORCELINK keyword required.\n",
  164. "A device cannot be deleted.",
  165. "Destination must be a directory.\n",
  166. "Infinite loop not allowed.\n",
  167. "Wildcard destination invalid.\n",
  168. };
  169.  
  170. LONG  CopyFile(ULONG, ULONG, ULONG);
  171. void  DoWork(STRPTR, struct CopyData *);
  172. LONG  IsPattern(STRPTR); /* return 0 -> NOPATTERN, return -1 --> ERROR */
  173. LONG  KillFile(STRPTR, ULONG);
  174. LONG  LinkFile(ULONG, STRPTR, ULONG);
  175. ULONG OpenDestDir(STRPTR, struct CopyData *);
  176. void  PatCopy(STRPTR, struct CopyData *);
  177. void  PrintName(STRPTR, ULONG, ULONG, ULONG);
  178. void  PrintNotDone(STRPTR, STRPTR, ULONG, ULONG);
  179. void  SetData(STRPTR, struct CopyData *);
  180. LONG  TestDest(STRPTR, ULONG, struct CopyData *);
  181. ULONG TestLoop(ULONG, ULONG);
  182.  
  183. struct DosLibrary *DOSBase;
  184.  
  185. ULONG start(void)
  186. {
  187.   struct Process *task;
  188.   struct DosLibrary *dosbase;
  189.   struct CopyData cd = {0,102400,0,0,0,0,0,COPYMODE_COPY,0,RETURN_FAIL,1};
  190.  
  191.   /* test for WB and reply startup-message */
  192.   if(!(task = (struct Process *) FindTask(0))->pr_CLI)
  193.   {
  194.     WaitPort(&task->pr_MsgPort);
  195.     Forbid();
  196.     ReplyMsg(GetMsg(&task->pr_MsgPort));
  197.     return RETURN_FAIL;
  198.   }
  199.   else if((dosbase = (struct DosLibrary *) OpenLibrary("dos.library", 37)))
  200.   {
  201.     STRPTR a[2] = {"", 0};
  202.     struct RDArgs *rda;
  203.     struct Args args = {0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0};
  204.  
  205.     DOSBase = dosbase;
  206.  
  207.     if((rda = (struct RDArgs *) AllocDosObject(DOS_RDARGS, 0)))
  208.     {
  209.       rda->RDA_ExtHelp =
  210.       "FROM     multiple input files\n"
  211.       "TO       destination file or directory\n"
  212.       "PATTERN  a pattern the filenames must match\n"
  213.       "BUFFER   buffersize for copy buffer (default 200 [100K])\n"
  214.       "ALL      deep scan into sub directories\n"
  215.       "DIRECT   copy mode only: copy file without any tests or options\n"
  216.       "CLONE    copy comment, protection bits and date as well\n"
  217.       "DATES    copy dates\n"
  218.       "NOPRO    do not copy protection bits\n"
  219.       "COMMENT  copy filecomment\n"
  220.       "QUIET    suppress all output and requesters\n"
  221.       "SILENT   suppress output, but not errors and requesters\n"
  222.       "NOREQ    suppress requesters\n"
  223.       "ERRWARN  do not proceed, when one file failed\n"
  224.       "MAKEDIR  produce directories\n"
  225.       "MOVE     delete source files after copying successful\n"
  226.       "DELETE   do not copy, but delete the source files\n"
  227.       "HARDLINK make a hardlink to source instead of copying\n"
  228.       "SOFTLINK make a softlink to source instead of copying\n"
  229.       "FOLNK    also makes links to directories\n"
  230.       "FODEL    delete protected files also\n"
  231.       "FOOVR    also overwrite protected files\n"
  232.       "DONTOVR  do never overwrite destination\n"
  233.       "FORCE    DO NOT USE. Call compatibility only.\n";
  234.  
  235.       if(ReadArgs(PARAM, (LONG *) &args, rda))
  236.       {
  237.         ULONG patbufsize = 0;
  238.         LONG i = 0;
  239.         APTR win = task->pr_WindowPtr;
  240.  
  241.         if(args.quiet) /* when QUIET, SILENT and NOREQ are also true! */
  242.           args.silent = args.noreq = 1;
  243.  
  244.         if(args.buffer && *args.buffer > 0) /* minimum buffer size */
  245.           cd.BufferSize = *args.buffer * 512;
  246.  
  247.         if(args.quiet)          cd.Flags |= COPYFLAG_QUIET;
  248.         if(args.silent)         cd.Flags |= COPYFLAG_SILENT;
  249.         if(args.all)            cd.Flags |= COPYFLAG_ALL;
  250.         if(args.clone)          cd.Flags |= COPYFLAG_DATES|COPYFLAG_COMMENT;
  251.         if(args.dates)          cd.Flags |= COPYFLAG_DATES;
  252.         if(args.comment)        cd.Flags |= COPYFLAG_COMMENT;
  253.         if(args.nopro)          cd.Flags |= COPYFLAG_NOPRO;
  254.         if(args.forcelink)      cd.Flags |= COPYFLAG_FORCELINK;
  255.         if(args.forcedelete)    cd.Flags |= COPYFLAG_FORCEDELETE;
  256.         if(args.forceoverwrite) cd.Flags |= COPYFLAG_FORCEOVERWRITE;
  257.         if(args.dontoverwrite)  cd.Flags |= COPYFLAG_DONTOVERWRITE;
  258.     if(args.errwarn)    cd.Flags |= COPYFLAG_ERRWARN;
  259.  
  260.     if(args.force) /* support OS Delete and MakeLink command options */
  261.     {
  262.       if(args.delete_mode)
  263.         cd.Flags |= COPYFLAG_FORCEDELETE;
  264.       if(args.hardlink || args.softlink)
  265.         cd.Flags |= COPYFLAG_FORCELINK;
  266.     }
  267.  
  268.         if(!args.from)  /* no args.from means currentdir */
  269.           args.from = a;
  270.  
  271.         if(args.noreq) /* no dos.library requests allowed */
  272.           task->pr_WindowPtr = (APTR) -1;
  273.  
  274.         if(args.delete_mode)    { ++i; cd.Mode = COPYMODE_DELETE; }
  275.         if(args.move_mode)      { ++i; cd.Mode = COPYMODE_MOVE; }
  276.     if(args.makedir)    { ++i; cd.Mode = COPYMODE_MAKEDIR; }
  277.         if(args.hardlink)       { ++i; cd.Mode = COPYMODE_LINK; }
  278.         if(args.softlink)
  279.         {
  280.           ++i;
  281.           cd.Mode = COPYMODE_LINK;
  282.           cd.Flags |= COPYFLAG_SOFTLINK|COPYFLAG_FORCELINK;
  283.         }
  284.  
  285.         if(cd.Mode != COPYMODE_DELETE && cd.Mode != COPYMODE_MAKEDIR &&
  286.         !args.to)
  287.         {
  288.           if(*(args.from+1)) /* when no TO is specified, the arg is last */
  289.           {                  /* one of from. Copy this argument into */
  290.             STRPTR *a;       /* args.to */
  291.  
  292.             a = args.from;
  293.             while(*(++a))
  294.               ;
  295.             args.to = *(--a); *a = 0;
  296.           }
  297.         }
  298.  
  299.         /* test if more than one of the above four or any other wrong
  300.            arguments */
  301.  
  302.     if(i > 1 ||
  303.     (args.from == a && cd.Mode == COPYMODE_MAKEDIR) ||
  304.     (args.direct && (cd.Mode != COPYMODE_COPY || args.from == a ||
  305.     !args.to || !*args.from || args.from[1] || cd.Pattern ||
  306.      (cd.Flags & ~(COPYFLAG_QUIET|COPYFLAG_SILENT|COPYFLAG_ERRWARN)))) ||
  307.     (args.dontoverwrite && args.forceoverwrite) ||
  308.     (args.nopro && args.clone) ||
  309.     (args.softlink && args.all) ||
  310.     (!args.to && cd.Mode != COPYMODE_DELETE && cd.Mode != COPYMODE_MAKEDIR))
  311.           SetIoErr(ERROR_TOO_MANY_ARGS);
  312.     else if(cd.Mode == COPYMODE_MAKEDIR)
  313.     {
  314.       ULONG i;
  315.       cd.RetVal2 = RETURN_OK;
  316.  
  317.           if(!args.silent)
  318.             Printf(texts[TEXTNUM_MODE+COPYMODE_MAKEDIR]);
  319.  
  320.       while(!cd.RetVal && !cd.RetVal2 && *args.from)
  321.       {
  322.             if((i = IsPattern(*args.from)))
  323.             {
  324.               if(i != -1)
  325.               {
  326.                 cd.RetVal = RETURN_ERROR;
  327.                 if(!args.quiet)
  328.                   Printf(TEXT_ERR_WILDCARD_DEST);
  329.               }
  330.               else
  331.                 cd.RetVal2 = RETURN_FAIL;
  332.             }
  333.         if((i = OpenDestDir(*args.from, &cd)))
  334.         {
  335.           UnLock(i);
  336.           cd.Flags |= COPYFLAG_DONE;
  337.         }
  338.         ++args.from;
  339.       }
  340.     } /* cd.Mode == COPYMODE_MAKEDIR */
  341.     else if(args.direct)
  342.     {
  343.       ULONG in, out;
  344.  
  345.       if((in = Open(*args.from, MODE_OLDFILE)))
  346.       {
  347.         if((out = Open(args.to, MODE_NEWFILE)))
  348.         {
  349.           cd.RetVal2 = CopyFile(in, out, cd.BufferSize);
  350.           Close(out);
  351.         }
  352.         Close(in);
  353.       }
  354.     }
  355.         else
  356.         {
  357.           if(args.pattern && *args.pattern)
  358.           {
  359.             patbufsize = (SDI_strlen(args.pattern)<<1) + 1;
  360.             if((cd.Pattern = (STRPTR) AllocMem(patbufsize, MEMF_PUBLIC)))
  361.             {
  362.               if(ParsePattern(args.pattern, cd.Pattern, patbufsize) < 0)
  363.               {
  364.                 FreeMem(cd.Pattern, patbufsize); cd.Pattern = 0;
  365.               }
  366.             }
  367.           }
  368.  
  369.           if((cd.Fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB, 0)))
  370.           {
  371.             if(!args.silent)
  372.               Printf(texts[TEXTNUM_MODE+cd.Mode+
  373.               (cd.Flags & COPYFLAG_SOFTLINK ? 1 : 0)]);
  374.  
  375.             if(args.pattern && !cd.Pattern)
  376.             {
  377.               if(!*args.pattern)
  378.                 SetIoErr(ERROR_BAD_TEMPLATE);
  379.             }
  380.             else if(cd.Mode == COPYMODE_DELETE)
  381.             {
  382.               cd.RetVal2 = RETURN_OK;
  383.  
  384.               while(cd.RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
  385.               && *args.from)
  386.                 PatCopy(*(args.from++), &cd);
  387.  
  388.             }
  389.             else if((i = IsPattern(args.to)))
  390.             {
  391.               if(i != -1)
  392.               {
  393.                 cd.RetVal = RETURN_ERROR;
  394.                 if(!args.quiet)
  395.                   Printf(TEXT_ERR_WILDCARD_DEST);
  396.               }
  397.             }
  398.             else
  399.             {
  400.               STRPTR path;
  401.  
  402.               if(*(path = PathPart(args.to)) == '/')
  403.                 ++path; /* is destination a path description ? */
  404.  
  405.               if(*path && !*(args.from+1) && !(i = IsPattern(*args.from)))
  406.               {
  407.                 ULONG lock;
  408.  
  409.                 /* is destination an existing directory */
  410.                 if((lock = Lock(args.to, SHARED_LOCK)))
  411.                 {
  412.                   if(Examine(lock, cd.Fib))
  413.                   {
  414.                     if(cd.Fib->fib_DirEntryType > 0)
  415.                       cd.RetVal2 = RETURN_OK;
  416.                     /* indicate dir-mode for next if */
  417.                   }
  418.                   else
  419.                     i = 1;
  420.                   UnLock(lock);
  421.                 }
  422.  
  423.                 /* is source a directory */
  424.                 if(!i && cd.RetVal2 && (lock = Lock(*args.from, SHARED_LOCK)))
  425.                 {
  426.                   if(Examine(lock, cd.Fib))
  427.                   {
  428.                     cd.RetVal2 = RETURN_OK;
  429.                     if(cd.Mode != COPYMODE_COPY ||
  430.                     cd.Fib->fib_DirEntryType < 0)
  431.                     {
  432.                       UBYTE sep;
  433.  
  434.                       cd.Flags |= COPYFLAG_DEST_FILE;
  435.                       /* produce missing destination directories */
  436.                       sep = *path; *path = 0;
  437.                       if((cd.CurDest = OpenDestDir(args.to, &cd)))
  438.                       {
  439.                         *path = sep;
  440.                         /* do the job */
  441.             UnLock(lock); lock = 0;
  442.             CopyMem(*args.from, cd.FileName, 1+strlen(*args.from));
  443.                         DoWork(FilePart(args.to), &cd); /* on file call */
  444.             UnLock(cd.CurDest);
  445.                       }
  446.                     }
  447.                   }
  448.                   if(lock)
  449.                     UnLock(lock);
  450.                 }
  451.               }
  452.               else if(i != -1)
  453.                 cd.RetVal2 = RETURN_OK;
  454.  
  455.               if(!cd.RetVal && !cd.RetVal2 && !(cd.Flags &
  456.               COPYFLAG_DEST_FILE) && (cd.Destination =
  457.               OpenDestDir(args.to, &cd)))
  458.               {
  459.                 while(cd.RetVal <= (args.errwarn ? RETURN_OK : RETURN_WARN)
  460.                 && *args.from && !CTRL_C)
  461.                   PatCopy(*(args.from++), &cd);
  462.  
  463.                 UnLock(cd.Destination);
  464.               }
  465.             } /* else */
  466.  
  467.             if(!(cd.Flags & COPYFLAG_DONE) && !args.silent && !cd.RetVal
  468.             && !cd.RetVal2)
  469.               Printf(TEXT_NOTHING_DONE);
  470.  
  471.             FreeDosObject(DOS_FIB, cd.Fib);
  472.           } /* if((cd.Fib = ... )) */
  473.  
  474.           if(cd.Pattern)
  475.             FreeMem(cd.Pattern, patbufsize);
  476.         } /* else */
  477.  
  478.     task->pr_WindowPtr = win;
  479.  
  480.         FreeArgs(rda);
  481.       } /* ReadArgs */
  482.       FreeDosObject(DOS_RDARGS, rda);
  483.     } /* AllocDosObject */
  484.  
  485.     if(!cd.RetVal2 && CTRL_C)
  486.     {
  487.       SetIoErr(ERROR_BREAK);
  488.       cd.RetVal2 = RETURN_WARN;
  489.     }
  490.  
  491.     if(cd.RetVal2 && !args.quiet && !cd.RetVal)
  492.       PrintFault(IoErr(),0);
  493.  
  494.     if(cd.RetVal)
  495.       cd.RetVal2 = cd.RetVal;
  496.  
  497.     CloseLibrary((struct Library *) dosbase);
  498.  
  499.     if(args.errwarn && cd.RetVal2 == RETURN_WARN)
  500.       cd.RetVal2 = RETURN_ERROR;
  501.   }
  502.  
  503.   return cd.RetVal2;
  504. }
  505.  
  506. void PatCopy(STRPTR name, struct CopyData *cd)
  507. {
  508.   struct AnchorPath *APath;
  509.   ULONG retval, doit = 0, deep = 0, failval = RETURN_WARN, first = 0;
  510.  
  511. #ifdef DEBUG
  512.   Printf("PatCopy(%s, .)\n", name);
  513. #endif
  514.  
  515.   if((cd->Mode == COPYMODE_COPY || (cd->Flags & COPYFLAG_ALL)) &&
  516.   !IsPattern(name))
  517.     first = 1; /* enter first directory (support of old copy style) */
  518.  
  519.   if(cd->Flags & COPYFLAG_ERRWARN)
  520.     failval = RETURN_OK;
  521.  
  522.   cd->CurDest = cd->Destination;
  523.   cd->DestPathSize = 0;
  524.  
  525.   if((APath = (struct AnchorPath *) AllocMem(sizeof(struct AnchorPath) + FILEPATH_SIZE,
  526.   MEMF_PUBLIC|MEMF_CLEAR)))
  527.   {
  528.     APath->ap_BreakBits = SIGBREAKF_CTRL_C;
  529.     APath->ap_Strlen = FILEPATH_SIZE;
  530.  
  531.     for(retval = MatchFirst(name, APath); !retval && cd->RetVal <=
  532.     failval && !cd->RetVal2; retval = MatchNext(APath))
  533.     {
  534.       if(doit)
  535.       {
  536.         DoWork(cd->Fib->fib_FileName, cd); doit = 0;
  537.       }
  538.  
  539.       if(deep)         /* used for Deep checking */
  540.       {
  541.         ++cd->Deep; deep = 0;
  542.       }
  543.  
  544.       cd->Flags &= ~COPYFLAG_ENTERSECOND;
  545.  
  546.       CopyMem(APath->ap_Buf, cd->FileName, FILEPATH_SIZE);
  547.       CopyMem(&APath->ap_Info, cd->Fib, sizeof(struct FileInfoBlock));
  548.  
  549.       if(first && APath->ap_Info.fib_DirEntryType > 0)
  550.         APath->ap_Flags |= APF_DODIR;
  551.       else if(APath->ap_Flags & APF_DIDDIR)
  552.       {
  553.     ULONG i;
  554.         cd->Flags |= COPYFLAG_ENTERSECOND;
  555.         APath->ap_Flags &= ~APF_DIDDIR;
  556.         --cd->Deep;
  557.         if(cd->Mode == COPYMODE_DELETE || cd->Mode == COPYMODE_MOVE)
  558.       doit = 1;
  559.     if((i = cd->CurDest))
  560.     {
  561.           cd->CurDest = ParentDir(i);
  562.       cd->DestPathSize = 0;
  563.       if(i != cd->Destination)
  564.             UnLock(i);
  565.           if(!cd->CurDest)
  566.             break;
  567.         }
  568.       }
  569.       else if(APath->ap_Info.fib_DirEntryType > 0)
  570.       {
  571.     doit = 1;
  572.         if(cd->Flags & COPYFLAG_ALL)
  573.         {
  574.           APath->ap_Flags |= APF_DODIR;
  575.           deep = 1;
  576.         }
  577.       }
  578.       else if(!cd->Pattern || MatchPatternNoCase(cd->Pattern,
  579.       APath->ap_Info.fib_FileName))
  580.         doit = 1;
  581.       first = 0;
  582.     }
  583.     MatchEnd(APath);
  584.  
  585.     if(retval != ERROR_NO_MORE_ENTRIES)
  586.       cd->RetVal2 = RETURN_FAIL;
  587.  
  588.     if(doit)
  589.       DoWork(cd->Fib->fib_FileName, cd);
  590.  
  591. /* No need to clear the flags here, as they are cleared on next PatJoin
  592.    call (DoWork is not called first round, as lock is zero!). */
  593.  
  594.     FreeMem(APath, sizeof(struct AnchorPath) + FILEPATH_SIZE);
  595.   }
  596.   else
  597.   {
  598.     cd->RetVal = RETURN_FAIL;
  599.     if(!cd->Flags & COPYFLAG_QUIET)
  600.       PrintFault(ERROR_NO_FREE_STORE, 0);
  601.   }
  602.  
  603.   if(cd->CurDest && cd->CurDest != cd->Destination)
  604.     UnLock(cd->CurDest);
  605. }
  606.  
  607. LONG IsPattern(STRPTR name)
  608. {
  609.   LONG a, ret = -1;
  610.   STRPTR buffer;
  611.  
  612.   a = (strlen(name)<<1) + 10;
  613.  
  614.   if((buffer = (STRPTR) AllocMem(a, MEMF_ANY)))
  615.   {
  616.     ret = ParsePattern(name, buffer, a);
  617.     FreeMem(buffer, a);
  618.   }
  619.  
  620.   if(ret == -1)
  621.     SetIoErr(ERROR_NO_FREE_STORE);
  622.  
  623.   return ret;
  624. }
  625.  
  626. LONG KillFile(STRPTR name, ULONG doit)
  627. {
  628.   if(doit)
  629.     SetProtection(name, 0);
  630.   return DeleteFile(name);
  631. }
  632.  
  633. ULONG OpenDestDir(STRPTR name, struct CopyData *cd)
  634. {
  635.   LONG a, err = 0, cr = 0;
  636.   STRPTR ptr = name;
  637.   UBYTE as;
  638.  
  639.   while(!err && *ptr != 0)
  640.   {
  641.     while(*ptr && *ptr != '/')
  642.       ++ptr;
  643.     as = *ptr;
  644.     *ptr = 0;
  645.  
  646.     if((a = TestDest(name, 1, cd)) == TESTDEST_CANTDELETE)
  647.     {
  648.       if(!(cd->Flags & COPYFLAG_QUIET))
  649.         Printf(TEXT_ERR_DEST_DIR);
  650.       err = 2;
  651.     }
  652.     else if(a < 0)
  653.       err = 1;
  654.     else if(a != TESTDEST_DIR_OK)
  655.     {
  656.       if((a = CreateDir(name)))
  657.       {
  658.     ++cr;
  659.         if(!(cd->Flags & COPYFLAG_SILENT))
  660.         {
  661.           PrintName(name, 1, 1, 1);
  662.           Printf("%s\n", TEXT_CREATED);
  663.         }
  664.         UnLock(a);
  665.       }
  666.       else
  667.       {
  668.         if(!(cd->Flags & COPYFLAG_QUIET))
  669.           PrintNotDone(name, TEXT_CREATED, 1, 1);
  670.         err = 2;
  671.       }
  672.     }
  673.     
  674.     *(ptr++) = as;
  675.   }
  676.  
  677.   if(err)
  678.   {
  679.     cd->RetVal = RETURN_ERROR;
  680.  
  681.     if(!(cd->Flags & COPYFLAG_QUIET) && err == 1)
  682.       PrintNotDone(name, TEXT_OPENED_FOR_OUTPUT, 1, 1);
  683.     return 0;
  684.   }
  685.  
  686.   if(cd->Mode == COPYMODE_MAKEDIR && !cr && !(cd->Flags & COPYFLAG_QUIET))
  687.   {
  688.     SetIoErr(ERROR_OBJECT_EXISTS);
  689.     PrintNotDone(name, TEXT_CREATED, 1, 1);
  690.   }
  691.  
  692.   return (ULONG) Lock(name, SHARED_LOCK);
  693. }
  694.  
  695. void PrintName(STRPTR name, ULONG deep, ULONG dir, ULONG txt)
  696. {
  697.   deep %= PRINTOUT_SPACES; /* reduce number of spaces */
  698.   /* This produces an error with MaxonC++ */
  699.  
  700.   while(deep--)
  701.     Printf(" ");
  702.  
  703.   if((deep = strlen(name)) > PRINTOUT_SIZE) /* reduce name size */
  704.   {
  705.     name += deep-PRINTOUT_SIZE;
  706.     Printf("...");
  707.   }
  708.  
  709.   Printf((dir ? TEXT_DIRECTORY : "%s"), name);
  710.   if(txt)
  711.     Printf(" ..");
  712.   Flush(Output());
  713. }
  714.  
  715. void PrintNotDone(STRPTR name, STRPTR txt, ULONG deep, ULONG dir)
  716. {
  717.   if(name)
  718.     PrintName(name, deep, dir, 1);
  719.   Printf(TEXT_NOT_DONE, txt);
  720.   PrintFault(IoErr(),0);
  721. }
  722.  
  723. void DoWork(STRPTR name, struct CopyData *cd)
  724. {
  725.   ULONG pdir, lock;
  726.   STRPTR printerr = 0, printok = "";
  727.  
  728.   if(cd->RetVal > (cd->Flags & COPYFLAG_ERRWARN ? RETURN_OK : RETURN_WARN)
  729.   || cd->RetVal2)
  730.     return;
  731.  
  732.   if(!(lock = Lock(cd->FileName, SHARED_LOCK)))
  733.   {
  734.     cd->RetVal = RETURN_WARN;
  735.     if(!(cd->Flags & COPYFLAG_QUIET))
  736.     {
  737.       PrintNotDone(cd->Fib->fib_FileName, TEXT_READ, cd->Deep,
  738.       cd->Fib->fib_DirEntryType > 0);
  739.     }
  740.     return;
  741.   }
  742.  
  743.   if(cd->Mode != COPYMODE_DELETE)
  744.   {
  745.     if(!cd->DestPathSize)
  746.     {
  747.       if(!NameFromLock(cd->CurDest, cd->DestName, FILEPATH_SIZE))
  748.       {
  749.         cd->RetVal2 = RETURN_FAIL;
  750.         UnLock(lock);
  751.         return;
  752.       }
  753.       cd->DestPathSize = strlen(cd->DestName);
  754.     }
  755.     cd->DestName[cd->DestPathSize] = 0;
  756.     AddPart(cd->DestName, name, FILEPATH_SIZE);
  757.   }
  758.  
  759.   if(!(pdir = ParentDir(lock)))
  760.   {
  761.     cd->RetVal = RETURN_ERROR;
  762.     if(cd->Mode == COPYMODE_DELETE)
  763.     {
  764.       if(!(cd->Flags & COPYFLAG_QUIET))
  765.       {
  766.         Printf(" %s ", cd->FileName);
  767.         Printf(TEXT_NOT_DONE, TEXT_DELETED);
  768.         Printf("%s\n", TEXT_ERR_DELETE_DEVICE);
  769.       }
  770.     }
  771.     UnLock(lock);
  772.     return;
  773.   }
  774.   UnLock(pdir);
  775.  
  776.   if(!(cd->Flags & COPYFLAG_SILENT))
  777.     PrintName(name, cd->Deep, cd->Fib->fib_DirEntryType > 0,
  778.     cd->Fib->fib_DirEntryType < 0 || (cd->Flags & COPYFLAG_ALL ?
  779.     cd->Mode != COPYMODE_DELETE : cd->Mode != COPYMODE_COPY) ||
  780.     cd->Flags & COPYFLAG_ENTERSECOND);
  781.  
  782.   if((cd->Flags & COPYFLAG_ENTERSECOND) || (cd->Mode == COPYMODE_DELETE
  783.   && (!(cd->Flags & COPYFLAG_ALL) || cd->Fib->fib_DirEntryType < 0)))
  784.   {
  785.     UnLock(lock); lock = 0;
  786.  
  787.     if(KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE))
  788.       printok = TEXT_DELETED;
  789.     else
  790.     {
  791.       cd->RetVal = RETURN_WARN;
  792.       printerr = TEXT_DELETED;
  793.     }
  794.   }
  795.   else if(cd->Mode == COPYMODE_DELETE)
  796.     ;
  797.   else if(cd->Fib->fib_DirEntryType > 0)
  798.   {
  799.     ULONG a;
  800.  
  801.     if((cd->Flags & COPYFLAG_ALL || cd->Mode == COPYMODE_LINK ||
  802.     cd->Mode == COPYMODE_MOVE) && TestLoop(lock, cd->CurDest))
  803.     {
  804.       printok = 0;
  805.       cd->RetVal = RETURN_ERROR;
  806.       if(!(cd->Flags & COPYFLAG_QUIET))
  807.       {
  808.         if(cd->Flags & COPYFLAG_SILENT)
  809.           PrintName(name, cd->Deep, 1, 1);
  810.         Printf(TEXT_NOT_DONE, TEXT_ENTERED);
  811.         Printf(TEXT_ERR_INFINITE_LOOP);
  812.       }
  813.     }
  814.     else if((a = TestDest(cd->DestName, 1, cd)) < 0)
  815.     {
  816.       printerr = TEXT_CREATED; cd->RetVal = RETURN_ERROR;
  817.     }
  818.     else if(cd->Flags & COPYFLAG_ALL)
  819.     {
  820.       ULONG i;
  821.  
  822.       i = cd->CurDest;
  823.       cd->DestPathSize = 0;
  824.  
  825.       if(a == TESTDEST_DIR_OK)
  826.       {
  827.         if(!(cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
  828.         {
  829.           printerr = TEXT_ENTERED; cd->RetVal = RETURN_ERROR;
  830.         }
  831.         else
  832.           printok  = TEXT_ENTERED;
  833.       }
  834.       else if((cd->CurDest = CreateDir(cd->DestName)))
  835.       {
  836.         UnLock(cd->CurDest);
  837.         if((cd->CurDest = Lock(cd->DestName, SHARED_LOCK)))
  838.           printok = TEXT_CREATED;
  839.         else
  840.         {
  841.           printerr = TEXT_ENTERED; cd->RetVal = RETURN_ERROR;
  842.         }
  843.       }
  844.       else
  845.       {
  846.         printerr = TEXT_CREATED; cd->RetVal = RETURN_ERROR;
  847.       }
  848.       if(!cd->CurDest)
  849.         cd->CurDest = i;
  850.       else if(i != cd->Destination)
  851.         UnLock(i);
  852.     }
  853.     else if(cd->Mode == COPYMODE_MOVE)
  854.     {
  855.       if(Rename(cd->FileName, cd->DestName))
  856.         printok = TEXT_RENAMED;
  857.       else
  858.       {
  859.         printerr = TEXT_RENAMED; cd->RetVal = RETURN_WARN;
  860.       }
  861.     }
  862.     else if(cd->Mode == COPYMODE_LINK)
  863.     {
  864.       if(!(cd->Flags & COPYFLAG_FORCELINK))
  865.       {
  866.         printok = 0;
  867.         cd->RetVal = RETURN_WARN;
  868.         if(!(cd->Flags & COPYFLAG_QUIET))
  869.         {
  870.           if(cd->Flags & COPYFLAG_SILENT)
  871.             PrintName(name, cd->Deep, 1, 1);
  872.           Printf(TEXT_NOT_DONE, TEXT_LINKED);
  873.           Printf(TEXT_ERR_FORCELINK);
  874.         }
  875.       }
  876.       else if(LinkFile(lock, cd->DestName, cd->Flags & COPYFLAG_SOFTLINK))
  877.         printok = TEXT_LINKED;
  878.       else
  879.       {
  880.         printerr = TEXT_LINKED; cd->RetVal = RETURN_WARN;
  881.       }
  882.     }
  883.     else /* COPY mode only displays directories, when not ALL */
  884.     {
  885.       printok = 0;
  886.       if(!(cd->Flags & COPYFLAG_SILENT))
  887.         Printf("\n");
  888.     }
  889.   }
  890.   else
  891.   {
  892.     /* test for existing destination file */
  893.     if(TestDest(cd->DestName, 0, cd) < 0)
  894.       printerr = TEXT_OPENED_FOR_OUTPUT;
  895.     else if(cd->Mode == COPYMODE_MOVE && Rename(cd->FileName, cd->DestName))
  896.       printok = TEXT_RENAMED;
  897.     else if(cd->Mode == COPYMODE_LINK)
  898.     {
  899.       if(!(cd->Flags & COPYFLAG_SOFTLINK) && LinkFile(lock, cd->DestName, 0))
  900.         printok = TEXT_LINKED;
  901.       else
  902.       {
  903.         printerr = TEXT_LINKED; cd->RetVal = RETURN_WARN;
  904.         if(cd->Flags & COPYFLAG_SOFTLINK)
  905.           SetIoErr(ERROR_OBJECT_WRONG_TYPE);
  906.       }
  907.     }
  908.     else
  909.     {
  910.       ULONG in, out, res = 0, h;
  911.       STRPTR txt = TEXT_OPENED_FOR_OUTPUT;
  912.  
  913.       if((out = Open(cd->DestName, MODE_NEWFILE)))
  914.       {
  915.         ULONG kill = 1;
  916.  
  917.     txt = cd->Mode == COPYMODE_MOVE ? TEXT_MOVED : TEXT_COPIED;
  918.     UnLock(lock); lock = 0;
  919.     if((in = Open(cd->FileName, MODE_OLDFILE)))
  920.     {
  921.       h = CopyFile(in, out, cd->BufferSize);
  922.       Close(in);
  923.       if(!h)
  924.       {
  925.         kill = 0;
  926.         if(cd->Mode == COPYMODE_MOVE)
  927.         {
  928.           if(KillFile(cd->FileName, cd->Flags & COPYFLAG_FORCEDELETE))
  929.                 res = 1;
  930.         }
  931.         else
  932.           res = 1;
  933.       }
  934.         }
  935.     Close(out);
  936.  
  937.     if(kill)
  938.       KillFile(cd->DestName, 0);
  939.       }
  940.       if(!res)
  941.       {
  942.     printerr = txt; cd->RetVal = RETURN_WARN;
  943.       }
  944.       else
  945.     printok = txt;
  946.     }
  947.   }
  948.       
  949.   if(printerr && !(cd->Flags & COPYFLAG_QUIET))
  950.     PrintNotDone(cd->Flags & COPYFLAG_SILENT ? name : 0,
  951.     printerr, cd->Deep, cd->Fib->fib_DirEntryType > 0);
  952.   else if(printok)
  953.   {
  954.     cd->Flags |= COPYFLAG_DONE;
  955.     if(!(cd->Flags & COPYFLAG_SILENT))
  956.       Printf("%s\n", printok);
  957.     SetData(cd->DestName, cd);
  958.   }
  959.  
  960.   if(lock)
  961.     UnLock(lock);
  962. }
  963.  
  964. LONG CopyFile(ULONG from, ULONG to, ULONG bufsize)
  965. {
  966.   STRPTR buffer;
  967.   LONG s, err = 0;
  968.  
  969.   if((buffer = (STRPTR) AllocMem(bufsize, MEMF_ANY)))
  970.   {
  971.     do
  972.     {
  973.       if((s = Read(from, buffer, bufsize)) == -1 ||
  974.       Write(to, buffer, s) == -1)
  975.         err = RETURN_FAIL;
  976.     } while(s == bufsize && !err);
  977.     FreeMem(buffer, bufsize);
  978.   }
  979.   else
  980.     err = RETURN_FAIL;
  981.  
  982.   return err;
  983. }
  984.  
  985. /* Softlink's path starts always with device name! f.e. "Ram Disk:T/..." */
  986. LONG LinkFile(ULONG from, STRPTR to, ULONG soft)
  987. {
  988.   if(soft)
  989.   {
  990.     UBYTE name[FILEPATH_SIZE];
  991.     NameFromLock(from, name, FILEPATH_SIZE);
  992.     return MakeLink(to, (ULONG) name, LINK_SOFT);
  993.   }
  994.   else
  995.     return MakeLink(to, from, LINK_HARD);
  996. }
  997.  
  998. /* return 0 means no loop, return != 0 means loop found */
  999. ULONG TestLoop(ULONG srcdir, ULONG destdir)
  1000. {
  1001.   ULONG par, lock, loop = 0;
  1002.  
  1003.   lock = destdir;
  1004.  
  1005.   if(SameDevice(srcdir, destdir))
  1006.   {
  1007.     do
  1008.     {
  1009.       if(!SameLock(srcdir, lock))
  1010.         loop = 1;
  1011.       else
  1012.       {
  1013.         par = ParentDir(lock);
  1014.         if(lock != destdir)
  1015.           UnLock(lock);
  1016.         lock = par;
  1017.       }
  1018.     } while(!loop && lock);
  1019.   }
  1020.  
  1021.   if(lock != destdir)
  1022.     UnLock(lock);
  1023.  
  1024.   return loop;
  1025. }
  1026.  
  1027. void SetData(STRPTR name, struct CopyData *cd)
  1028. {
  1029.   if(cd->Flags & COPYFLAG_NOPRO)
  1030.     SetProtection(name, 0);
  1031.   else
  1032.     SetProtection(name, cd->Fib->fib_Protection);
  1033.   if(cd->Flags & COPYFLAG_DATES)
  1034.     SetFileDate(name, &cd->Fib->fib_Date);
  1035.   if(cd->Flags & COPYFLAG_COMMENT)
  1036.     SetComment(name, cd->Fib->fib_Comment);
  1037. }
  1038.  
  1039. LONG TestDest(STRPTR name, ULONG type, struct CopyData *cd)
  1040. {
  1041.   LONG ret = TESTDEST_ERROR;
  1042.   ULONG lock;
  1043.  
  1044.   if((lock = Lock(name, SHARED_LOCK)))
  1045.   {
  1046.     struct FileInfoBlock *fib;
  1047.  
  1048.     if((fib = (struct FileInfoBlock *) AllocDosObject(DOS_FIB, 0)))
  1049.     {
  1050.       if(Examine(lock, fib))
  1051.       {
  1052.         UnLock(lock); lock = 0;
  1053.         if(type)
  1054.         {
  1055.           if(fib->fib_DirEntryType > 0)
  1056.             ret = TESTDEST_DIR_OK;
  1057.           else if(!(cd->Flags & COPYFLAG_DONTOVERWRITE))
  1058.           {
  1059.             if(KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE))
  1060.               ret = TESTDEST_DELETED;
  1061.           }
  1062.           else
  1063.             ret = TESTDEST_CANTDELETE;
  1064.         }
  1065.         else if(cd->Flags & COPYFLAG_DONTOVERWRITE)
  1066.           ret = TESTDEST_CANTDELETE;
  1067.         else if(KillFile(name, cd->Flags & COPYFLAG_FORCEOVERWRITE))
  1068.           ret = TESTDEST_DELETED;
  1069.       }
  1070.       FreeDosObject(DOS_FIB, fib);
  1071.     }
  1072.     if(lock)
  1073.       UnLock(lock);
  1074.   }
  1075.   else
  1076.     ret = TESTDEST_NONE;
  1077.  
  1078.   if(ret == TESTDEST_CANTDELETE)
  1079.     SetIoErr(ERROR_OBJECT_EXISTS);
  1080.  
  1081.   return ret;
  1082. }
  1083.  
  1084.